﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Jay.Sc2.Bof.Lib;
using Jay.Sc2.Bof.Lib.Action;
using Jay.Sc2.Bof.Lib.State;
using Jay.Sc2.Bof.Lib.Tools;

namespace Jay.Sc2.Bof.Windows
{

	public partial class GenerationForm : Form
	{
		RaceSelectorForm m_RaceSelector;
		BuildOrderFinder m_Finder;
		public BuildOrderFinder Finder { get { return m_Finder; } }
		IBuildOrder m_BuildOrder;
		Thread m_Thread;
		bool m_Running;
		public bool Running { get { return m_Running; } }

		public GenerationForm()
		{
			InitializeComponent();
			IState state = Program.CurrentRace.EmptyState;
			state.Time = 20 * 60;
			IStateProxy proxy = Program.GetProxy(state);
			proxy.ShowReadOnlyProperties = false;
			pgGoal.SelectedObject = proxy;
			pgState.SelectedObject = null;
			string icon = GetType().Namespace + "." + Program.CurrentRace.Name.ToLower() + ".ico";
			Stream iconStream = GetType().Assembly.GetManifestResourceStream(icon);
			Icon = new Icon(iconStream);
		}

		public GenerationForm(RaceSelectorForm raceSelectorForm) : this()
		{
			m_RaceSelector = raceSelectorForm;
		}

		private void bFind_Click(object sender, EventArgs e)
		{
			if (m_Running)
			{
				m_Running = false;
				bFind.Enabled = false;
				bFind.Text = "Stopping...";
			}
			else
			{
				bFind.Text = "Stop";
				m_Thread = new Thread(new ThreadStart(Run));
				m_Thread.Start();
			}
		}

		private void PercentageChanged(double percentage)
		{
			Invoke(new MethodInvoker(delegate()
				{
					tspbPercentage.Value = (int)(percentage * 100);
					tspbPercentage.Maximum = 100;
					tsslStatus.Text = string.Format("Generation #{0}, {1} BuildOrders{2}.", m_Finder.m_Generation, m_Finder.m_GameTested, m_StabilityCounter > 1 ? string.Format(", Stable for {0} generations", m_StabilityCounter) : "");
				}));
		}

		public static void AddListViewItemLine(IBuildOrder build, IAction action, ref int count, ref int lastCount, ref int lastTime, ListView lvActionList)
		{
			if (build.ActionList.Contains(action)) count++;
			ListViewItem item = new ListViewItem(new string[] {
				count != lastCount ? count.ToString() : "",
				build.State.Time != lastTime ? new TimeSpan(0,0,build.State.Time).ToString() : "",
				action.ToString(),
				((int)build.State.Minerals).ToString(),
				((int)build.State.Gas).ToString(),
				string.Format("{0}/{1}",build.State.Food,build.State.Supply),
				string.Format("{0} ({1}/{2})", build.State.Workers, build.State.WorkersOnMinerals, build.State.WorkersOnGas),
				build.State.Army.ToString()

			});
			lastCount = count;
			lastTime = build.State.Time;
			item.ForeColor = ActionColor(action.Name);
			lvActionList.Items.Add(item);
		}

		public static void Run(int time, IActionList actionList, PropertyGrid propertyGrid)
		{
			IStateProxy proxy;
			IState initial = Program.CurrentRace.InitialState;
			BuildOrder bo = new BuildOrder(initial.Race, initial, null, new ActionList(actionList));
			int count = 0;
			int lastCount = 0;
			int lastTime = -1;
			int currentGoal = -1;
			for (int c = 0; c < actionList.Count; c++)
			{
				IAction action = actionList[c];
				if (action.IsInvalid(bo))
				{
					actionList.RemoveAt(c);
					c--;
				}
				else
				{
					while (!action.CanExecute(bo))
					{
						if (bo.State.Time >= time)
						{
							proxy = Program.GetProxy(bo.State);
							proxy.ShowOnlyIfPresent = true;
							propertyGrid.SelectedObject = proxy;
							return;
						}
						GenerationForm.IncrementTime(bo, ref count, ref lastCount, ref lastTime, null, ref currentGoal);
					}
					action.Execute(bo);
				}
			}
			while (bo.State.Time < time)
				GenerationForm.IncrementTime(bo, ref count, ref lastCount, ref lastTime, null, ref currentGoal);
			proxy = Program.GetProxy(bo.State);
			proxy.ShowOnlyIfPresent = true;
			propertyGrid.SelectedObject = proxy;
		}

		public static void IncrementTime(IBuildOrder bo, ref int count, ref int lastCount, ref int lastTime, ListView lvActionList, ref int currentGoal)
		{
			bo.State.IncrementTime();
			ICollection<IAction> autoActions = bo.GetActions(bo.State.Time);
			if (autoActions != null)
				foreach (IAction auto in new ActionList(bo.Race, autoActions))
				{
					auto.Execute(bo);
					if(lvActionList != null) GenerationForm.AddListViewItemLine(bo, auto, ref count, ref lastCount, ref lastTime, lvActionList);
				}
			if (lvActionList != null)
			{
				while (bo.Goals != null && bo.Goals.Count > currentGoal && bo.State.IsSatisfying(bo.Goals[currentGoal]))
				{
					currentGoal++;
					ListViewItem item = new ListViewItem(new string[] {
					"",
					bo.State.Time != lastTime ? new TimeSpan(0,0,bo.State.Time).ToString() : "",
					"--- Goal #" + currentGoal.ToString() + " ---",
					((int)bo.State.Minerals).ToString(),
					((int)bo.State.Gas).ToString(),
					string.Format("{0}/{1}",bo.State.Food,bo.State.Supply),
					string.Format("{0} ({1}/{2})", bo.State.Workers, bo.State.WorkersOnMinerals, bo.State.WorkersOnGas),
					bo.State.Army.ToString()
				});
					lastCount = count;
					lastTime = bo.State.Time;
					item.ForeColor = Color.Red;
					item.BackColor = Color.LightGray;
					lvActionList.Items.Add(item);
				}
			}
		}

		private IGoal Goal
		{
			get
			{
				IGoal goal = new Goal();
				for (int c = 0; c < tabControl.TabPages.Count; c++)
				{
					Control[] controls = tabControl.TabPages[c].Controls.Find("pgGoal", false);
					if (controls != null && controls.Length == 1)
					{
						PropertyGrid pgGoal = controls[0] as PropertyGrid;
						goal.Add((pgGoal.SelectedObject as IStateProxy).State);
					}
				}
				return goal;
			}
		}

		private int m_StabilityCounter = 0;
		private void Run()
		{
			this.Invoke(new MethodInvoker(delegate()
			{
				configurationToolStripMenuItem.Enabled = false;
			}));
			m_StabilityCounter = 0;
			m_Running = true;
			BuildOrderFinder old = m_Finder;
			IGoal goal = Goal;
			m_Finder = new BuildOrderFinder(Program.CurrentRace, goal);
			if (old != null)
			{
				m_Finder.m_MaxActionListSize = old.m_MaxActionListSize;
				m_Finder.m_FailedCount = old.m_FailedCount;
				m_Finder.m_SubsetSize = old.m_SubsetSize;
				m_Finder.m_Mutaters = old.m_Mutaters;
				m_Finder.m_Reproducers = old.m_Reproducers;
				m_Finder.m_AutoActions = old.m_AutoActions;
				m_Finder.m_Scouting = old.m_Scouting;
			}

			pbPercentage.Invoke(new MethodInvoker(delegate()
			{
				pbPercentage.Maximum = (int)goal.MaximumScore;
			}));

			IActionList actionList = null;
			while (m_Running)
			{
				m_StabilityCounter++;
				tsslStatus.Text = string.Format("Generation #{0}, {1} BuildOrders{2}.", m_Finder.m_Generation, m_Finder.m_GameTested, m_StabilityCounter > 1 ? string.Format(", Stable for {0} generations", m_StabilityCounter) : "");
				IBuildOrder build = m_Finder.NextGeneration(PercentageChanged);
				List<ListViewItem> items = new List<ListViewItem>();
				if (actionList != null && actionList.Equals(build.ActionList))
					continue;
				m_BuildOrder = build;
				m_StabilityCounter = 0;
				actionList = new ActionList(build.ActionList);
				actionList.Save("LastActionList.xml");
				int count = 0;
				int lastCount = 0;
				int lastTime = -1;
				if (InvokeRequired)
				{
					lvActionList.Invoke(new MethodInvoker(delegate()
					{
						List<Scouting> scouting = new List<Scouting>(m_Finder.m_Scouting);
						IState copy = m_Finder.Race.InitialState;
						BuildOrder bo = new BuildOrder(m_Finder.Race, copy, build.Goals, build.ActionList);
						lvActionList.Items.Clear();
						lvActionList.Tag = build.ActionList;
						int previousTime = -1;
						int currentGoal = 0;
						while (bo.State.Time <= build.State.Time)
						{
							//bo.State.CheckForScouting(bo, scouting);
							if (previousTime == bo.State.Time)
							{
								IncrementTime(bo, ref count, ref lastCount, ref lastTime, lvActionList, ref currentGoal);
								//bo.State.CheckForScouting(bo, scouting);
							}
							ICollection<IAction> actions = build.GetActions(bo.State.Time);
							previousTime = bo.State.Time;
							if (bo.State.Time <= build.State.Time && actions != null && actions.Count > 0)
							{
								foreach (IAction action in actions)
								{
									if (build.ActionList.Contains(action))
									{
										if (!action.IsInvalid(bo))
										{
											while (!action.CanExecute(bo))
											{
												IncrementTime(bo, ref count, ref lastCount, ref lastTime, lvActionList, ref currentGoal);
												//bo.State.CheckForScouting(bo, scouting);
											}
											action.Execute(bo);
											AddListViewItemLine(bo, action, ref count, ref lastCount, ref lastTime, lvActionList);
										}
										else
										{
											MessageBox.Show("Hum ...");
										}
									}
								}
							}
						}
					}));
					pgState.Invoke(new MethodInvoker(delegate()
					{
						IStateProxy proxy;
						proxy = Program.GetProxy(build.State);
						proxy.ShowOnlyIfPresent = true;
						pgState.SelectedObject = proxy;
					}));
					pbPercentage.Invoke(new MethodInvoker(delegate()
					{
						pbPercentage.Value = Math.Min(pbPercentage.Maximum, (int)build.Score);
						toolTip.SetToolTip(pbPercentage, pbPercentage.Value + "/" + pbPercentage.Maximum);
					}));
				}
			}
			bFind.Invoke(new MethodInvoker(delegate()
			{
				bFind.Text = "Find";
				bFind.Enabled = true;
			}));
			this.Invoke(new MethodInvoker(delegate()
			{
				configurationToolStripMenuItem.Enabled = true;
			}));
		}

		private static bool StartsWith(string text, string actionName)
		{
			if (actionName.StartsWith(text) && actionName.Length > text.Length && char.IsUpper(actionName[text.Length])) return true;
			return false;
		}

		public static Color ActionColor(string actionName)
		{
			if (StartsWith("Action", actionName)) actionName = actionName.Substring(6);
			if (StartsWith("Produce", actionName)) return Color.Black;
			if (StartsWith("Spawn", actionName)) return Color.Gray;
			if (StartsWith("Harvest", actionName)) return Color.Green;
			if (StartsWith("Upgrade", actionName)) return Color.Blue; ;
			if (StartsWith("Researched", actionName)) return Color.LightBlue;
			if (StartsWith("Switch", actionName)) return Color.Orange;
			if (StartsWith("Trick", actionName)) return Color.Red;
			return Color.DarkGray;
		}

		private void GenerationForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			m_Running = false;
			if (m_Thread != null && m_Thread.IsAlive) m_Thread.Abort();
			if (m_RaceSelector != null) m_RaceSelector.Show();
		}

		public static void CopyListViewToClipboard(ListView lvActionList)
		{
			StringBuilder builder = new StringBuilder();
			foreach (ListViewItem item in lvActionList.Items)
			{
				bool coma = false;
				for (int c = 0; c < lvActionList.Columns.Count; c++)
				{
					if (!coma) coma = true;
					else builder.Append("\t");
					builder.Append(item.SubItems[c].Text);
				}
				builder.AppendLine();
			}
			Clipboard.SetText(builder.ToString());
		}

		public static void CopyShortDescriptionToClipboard(ActionList actionList)
		{
			if(actionList != null)
				Clipboard.SetText(actionList.ShortDescription);
		}

		private void actionListToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ActionListForm form = new ActionListForm(m_BuildOrder == null ? null : m_BuildOrder.ActionList);
			form.Icon = Icon;
			form.Show(this);
		}

		private void configurationToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (m_Finder == null)
			{
				m_Finder = new BuildOrderFinder(Program.CurrentRace, Goal);
			}
			ConfigurationForm form = new ConfigurationForm(m_Finder);
			form.Icon = Icon;
			form.Show(this);
		}

		private void bAddGoal_Click(object sender, EventArgs e)
		{
			tabControl.TabPages.Add("Goal #" + (tabControl.TabPages.Count + 1));
			TabPage page = tabControl.TabPages[tabControl.TabPages.Count - 1];
			PropertyGrid pgGoal = new PropertyGrid();
			pgGoal.Name = "pgGoal";
			IState state = Program.CurrentRace.EmptyState;
			state.Time = 20 * 60;
			IStateProxy proxy;
			proxy = Program.GetProxy(state);
			proxy.ShowReadOnlyProperties = false;
			pgGoal.SelectedObject = proxy;
			page.Controls.Add(pgGoal);
			pgGoal.Location = new Point(this.pgGoal.Location.X, this.pgGoal.Location.Y);
			pgGoal.Size = new Size(this.pgGoal.Size.Width, this.pgGoal.Size.Height);
			pgGoal.Anchor = this.pgGoal.Anchor;
		}

		private void shortToolStripMenuItem_Click(object sender, EventArgs e)
		{
			CopyShortDescriptionToClipboard(lvActionList.Tag as ActionList);
		}

		private void detailledToolStripMenuItem_Click(object sender, EventArgs e)
		{
			CopyListViewToClipboard(lvActionList);
		}

		private void lvActionList_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (lvActionList.SelectedIndices.Count > 0)
			{
				int index = lvActionList.SelectedIndices[0];
				while (index > 0 && string.IsNullOrWhiteSpace(lvActionList.Items[index].SubItems[1].Text))
					index--;
				TimeSpan span;
				if (TimeSpan.TryParse(lvActionList.Items[index].SubItems[1].Text, out span))
					GenerationForm.Run((int)span.TotalSeconds, lvActionList.Tag as IActionList, pgState);
			}
		}
	}
}
